package me.luger.project.fof.service;

import me.luger.core.commom.util.DateUtil;
import me.luger.core.dao.nutz.service.BaseService;
import me.luger.project.fof.entity.*;
import me.luger.project.fof.service.FofResultService;
import org.apache.commons.collections.map.HashedMap;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.*;

@Repository
public class FofWeightDailyService extends BaseService<FofWeightDaily> {
    public FofWeightDailyService(Dao dao) {
        super(dao);
    }


    @Autowired
    private FundInfoService fundInfoService;

    @Autowired
    private FundNavService fundNavService;

    @Autowired
    private AssetStrategyInfoService assetStrategyInfoService;

    private Map<Integer, AssetStrategyInfo> assetStrategyMap = null;

    /**
     * 获取FOF对应所有子基金每日权重的列表
     * @param   fofId
     * @return  子基金每日权重的列表
     */
    public List<FofWeightDaily> getFofWeightDailyByFofId(Integer fofId){
        Sql sql = Sqls.create("select fof_id, fund_ext_code, weight, calc_date from fof_weight_daily where fof_id = @fofId order by calc_date");
        sql.params().set("fofId",fofId);
        List<FofWeightDaily> fwdList = this.list(sql);
        return fwdList;
    }

    public Date calcFundStartDate(List<FofResult> firstFrList, Date startDate, Date endDate) {
        Date firstStartDay = startDate;
        for(FofResult fr : firstFrList) {
            String fec = fr.getFundExtCode();
            Date fundDay = null;
            FundNav fn = fundNavService.getFundNavFirstDay(fec);
            if(fn != null) {
                fundDay = fn.getStatisticDate();
            }
            //把firstStartDay设置为子基金中净值起始点最晚的那天，并以此作为回测的起始日期
            if(fundDay != null && fundDay.after(firstStartDay)) {
                firstStartDay = fundDay;
            }
        }
        return firstStartDay;
    }

    /**
     * 获取Fof组合对应的子基金的净值序列
     * @param firstFrList
     * @param startDate
     * @param endDate
     * @return
     */
    public Map<String, List<FundNav>> getFofFundNavMap(List<FofResult> firstFrList, Date startDate, Date endDate) {

        //调整FOF回测的起始日期，为子基金中净值起始点最晚的那天
        Date firstStartDay = calcFundStartDate(firstFrList, startDate, endDate);

        //获取FOF对应基金的净值历史
        Map<String, List<FundNav>> fundTrendMap = null;
        for(FofResult fr : firstFrList) {
            String fundExtCode = fr.getFundExtCode();
            FundNav fundNav = fundNavService.fetchById(fundExtCode);
            if (null == fundNav) {
                continue;
            }
            List<FundNav> fundNavList = null;

            fundNavList = fundNavService.getFundNavTrendByDateRange(fundExtCode, firstStartDay, endDate);
            fundNavList = fundNavService.fullFundNavList(fundNavList, firstStartDay, endDate);
            if(fundTrendMap == null) {
                fundTrendMap = new HashedMap();
            }
            fundTrendMap.put(fundExtCode, fundNavList);
        }
        return fundTrendMap;
    }

    /**
     * 获取FOF对应所有子基金每日权重
     * @param   fofId
     * @return  Map，key为基金id，value为该基金每日权重的列表
     */
    public Map<String,List<FofWeightDaily>> getFofWeightDaily(Integer fofId, List<FofResult> firstFrList, Date startDate, Integer backTestYear, Boolean calcAssetConf) {

        if(fofId == null) {
            return null;
        }
        if(backTestYear == null) {
            backTestYear = 1;
        }

        //所有历史FOF的组成，按照时间顺序放在fofDateMap中
        //计算规则为：FOF回测净值为最早一个FOF组合往前回测
        //结束时间为当前时间
        Date endDate = new Date();

        //调整回测时间
        Calendar c = Calendar.getInstance();
            c.setTime(startDate);
            c.add(Calendar.YEAR, backTestYear * -1);
        startDate = c.getTime();

        //获取子基金的信息
        Map<String, FundInfo> fiMap = new HashedMap();
        for(FofResult fr : firstFrList) {
            FundInfo fi = fundInfoService.getFundInfo(fr.getFundExtCode());
            //将FundInfo中的资产大类信息替换为保存FOF时选择的资产大类的来源，by xujz，20170722
            fi.setAssetStrategyId(fr.getAssetStrategyId());
            fiMap.put(fr.getFundExtCode(), fi);
        }

        Map<String, List<FundNav>> fundTrendMap = getFofFundNavMap(firstFrList, startDate, endDate);
        //组合子基金，以日期为key放在map中
        Map<String, Map<String, FundNav>> dateFundNavMap = new TreeMap<String, Map<String, FundNav>>();
        for(Map.Entry<String, List<FundNav>> k : fundTrendMap.entrySet()) {
            List<FundNav> fnList = k.getValue();
            for(FundNav fn : fnList) {
                Date dt = fn.getStatisticDate();
                String d = DateUtil.format(dt, DateUtil.DATE_YYYY_MM_DD);
                Map<String, FundNav> fnl = dateFundNavMap.get(d);
                if(fnl == null) {
                    fnl = new HashedMap();
                }
                fnl.put(fn.getFundExtCode(), fn);
                dateFundNavMap.put(d, fnl);
            }
        }

        //key为FOF对应基金的fund_ext_code，value为该基金的每日权重
        Map<String,Map<Date, FofWeightDaily>> fwdMap = new TreeMap();

        //计算子基金权重=子基金份额*净值/SUM（子基金份额*权重）
        for(Map.Entry<String, Map<String, FundNav>> k : dateFundNavMap.entrySet()) {
            String d = k.getKey();
            Map<String, FundNav> fnMap = k.getValue();
            //计算SUM（子基金份额*权重）
            Double sum = new Double(0);
            for(Map.Entry<String, FundNav> ke : fnMap.entrySet()) {
                FundNav fn = ke.getValue();
                Double quantity = new Double(0);
                for(FofResult fr : firstFrList) {
                    //找到子基金对应的portion
                    if(fr.getFundExtCode().equals(fn.getFundExtCode())) {
                        quantity = fr.getFundQuantity();
                        continue;
                    }
                }
                sum += fn.getSwanav() * quantity;
            }

            //计算各子基金的权重
            if(!calcAssetConf) {
                for(Map.Entry<String, FundNav> ke : fnMap.entrySet()) {
                    FundNav fn = ke.getValue();
                    Double weight = new Double(0);
                    Double quantity = new Double(0);
                    for(FofResult fr : firstFrList) {
                        //找到子基金对应的portion
                        if(fr.getFundExtCode().equals(fn.getFundExtCode())) {
                            quantity = fr.getFundQuantity();
                            continue;
                        }
                    }
                    if(sum > 0) {
                        weight = fn.getSwanav() * quantity / sum;
                    }
                    FofWeightDaily fwd = new FofWeightDaily();
                    fwd.setFofId(fofId);
                    fwd.setCalcDate(DateUtil.parseDate(d, DateUtil.DATE_YYYY_MM_DD));
                    fwd.setWeight(weight);
                    fwd.setFundExtCode(fn.getFundExtCode());
                    String fname = "默认名称";
                    FundInfo fi = fundInfoService.getFundInfo(fn.getFundExtCode());
                    if(fi != null) {
                        fname = fi.getFundNameShort();
/*                    if(fname == null || fname.equals("")) {
                            fname = fi.getFundNameFull();
                        }*/
                    }
                    Map<Date, FofWeightDaily> tmpList = fwdMap.get(fname);
                    if(tmpList == null || tmpList.size() < 1) {
                        tmpList = new TreeMap<Date, FofWeightDaily>();
                    }
                    tmpList.put(fwd.getCalcDate(), fwd);
                    fwdMap.put(fname, tmpList);
                }
            }//end 计算各子基金的权重

            //计算各子基金对应资产大类的权重，需要对相同大类的基金合并到一个大类中
            else {
                //为了方便起见，拿FofWeightDaily对象保存资产大类的净值
                Map<String, FofWeightDaily> assetConfMap = new HashedMap();

                if(assetStrategyMap == null) {
                    List<AssetStrategyInfo> asiList = assetStrategyInfoService.getAllAssetStrategyInfo();
                    if(asiList == null || asiList.size() <1) {
                        //获取资产大类列表错误……
                        return null;
                    }
                    assetStrategyMap = new HashedMap();
                    for(AssetStrategyInfo tmpAsi : asiList) {
                        assetStrategyMap.put(tmpAsi.getId(), tmpAsi);
                    }
                }

                for(Map.Entry<String, FundNav> ke : fnMap.entrySet()) {
                    FundNav fn = ke.getValue();
                    FundInfo fi = fiMap.get(fn.getFundExtCode());
                    Integer assetStrategyId = fi.getAssetStrategyId();//TODO 修改大类资产的获取方式
                    AssetStrategyInfo asi = assetStrategyMap.get(assetStrategyId);
                    if(asi == null) {
                        asi = new AssetStrategyInfo();
                        asi.setName("未知");
                        asi.setId(0);
                    }
                    FofWeightDaily fwd = assetConfMap.get(asi.getId().toString());
                    if(fwd == null) {
                        fwd = new FofWeightDaily();
                        fwd.setFundExtCode(asi.getId().toString());
                        fwd.setCalcDate(fn.getStatisticDate());
                        fwd.setWeight(new Double(0));
                    }
                    //计算权重，资产大类权重 = 该大类下所有基金的和
                    Double weight = new Double(0);
                    Double quantity = new Double(0);
                    for(FofResult fr : firstFrList) {
                        //找到子基金对应的portion
                        if(fr.getFundExtCode().equals(fn.getFundExtCode())) {
                            quantity = fr.getFundQuantity();
                            continue;
                        }
                    }
                    if(sum > 0) {
                        weight = fwd.getWeight() + fn.getSwanav() * quantity / sum;
                        fwd.setWeight(weight);
                    }
                    assetConfMap.put(fwd.getFundExtCode(), fwd);
                    Map<Date, FofWeightDaily> tmpList = fwdMap.get(asi.getName());
                    if(tmpList == null || tmpList.size() < 1) {
                        tmpList = new TreeMap<Date, FofWeightDaily>();
                    }
                    tmpList.put(fwd.getCalcDate(), fwd);
                    fwdMap.put(asi.getName(), tmpList);
                }
            }//end 计算各子基金对应资产大类的权重
        }
        //key为FOF对应基金的fund_ext_code，value为该基金的每日权重
        Map<String,List<FofWeightDaily>> fwdMapListMap = new TreeMap();
        for(Map.Entry<String,Map<Date, FofWeightDaily>> wmle : fwdMap.entrySet()) {
            String e = wmle.getKey();
            Map<Date, FofWeightDaily> fm = wmle.getValue();
            List<FofWeightDaily> lf = new ArrayList<FofWeightDaily>();
            for(Map.Entry<Date, FofWeightDaily> dfwd : fm.entrySet()) {
                lf.add(dfwd.getValue());
            }
            fwdMapListMap.put(e,lf);
        }
        return fwdMapListMap;

    }

    public Map<String,List<FofWeightDaily>> getFofWeightDaily(Integer fofId, List<FofResult> firstFrList, Date startDate, Integer backTestYear) {
        return getFofWeightDaily(fofId, firstFrList,startDate, backTestYear, false);
    }

    /**
     * 获取FOF子基金权重，默认回测1年
     * @param fofId
     * @return
     */
    public Map<String,List<FofWeightDaily>> getFofWeightDaily(Integer fofId, List<FofResult> firstFrList, Date startDate) {
        return getFofWeightDaily(fofId, firstFrList,startDate, 1, false);
    }

    /**
     * 返回FOF每日净值
     * @param fofId
     * @return
     */
    public Map<String,List<FofWeightDaily>> getFofNavDaily(Integer fofId) {
        //获取FOF对应历史上的基金每日净值list
        List<FofWeightDaily> fwdList = getFofWeightDailyByFofId(fofId);
        //key为FOF对应基金的fund_ext_code，value为该基金的每日权重
        TreeMap<String,List<FofWeightDaily>> fwdMap = new TreeMap<String,List<FofWeightDaily>>();

        for (FofWeightDaily fwd : fwdList) {
            String fundExtCode = fwd.getFundExtCode();
            List<FofWeightDaily> fwdList2 = fwdMap.get(fundExtCode);
            if(fwdList2 == null || fwdList2.size() < 1) {
                fwdList2 = new ArrayList<FofWeightDaily>();
            }
            fwdList2.add(fwd);
            fwdMap.put(fundExtCode, fwdList2);
        }
        return fwdMap;

    }


}
